﻿using System;
using System.Collections.Generic;
using System.IO.Ports;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading.Tasks;

namespace SmartPowerHub.Communication.Modbus
{
    public class ModbusRtuHelper
    {
        private EndianFormat _endianFormat;

        private StringFormat _stringFormat;

        public ModbusRtuHelper(EndianFormat endianFormat, StringFormat stringFormat)
        {
            _endianFormat = endianFormat;
            _stringFormat = stringFormat;
        }

        /// <summary>
        /// 构建请求报文
        /// </summary>
        /// <param name="functionCode"></param>
        /// <param name="startAddress"></param>
        /// <param name="values"></param>
        /// <param name="slaveId"></param>
        /// <returns></returns>
        public byte[] BuildWriteRequest<T>(FunctionCode functionCode, ushort startAddress, byte[] dataBuffer, byte slaveAddress)
        {
            List<byte> frame = new List<byte>();
            frame.Add(slaveAddress);
            frame.Add((byte)functionCode);

            frame.Add(BitConverter.GetBytes(startAddress)[1]);
            frame.Add(BitConverter.GetBytes(startAddress)[0]);
            if (dataBuffer.Length > 2)
            {
                // 帧结构：从站地址(1) + 功能码(1) + 起始地址(2) + 寄存器数量(2) +  字节计数(1) + 数据(n*2) + CRC(2)
                int count = dataBuffer.Length / 2; //每个寄存器2字节
                frame.Add(BitConverter.GetBytes(count)[1]);//寄存器数量
                frame.Add(BitConverter.GetBytes(count)[0]);

                frame.Add((byte)(dataBuffer.Length));// 字节计数
                frame.AddRange(dataBuffer);
            }
            else
            {
                // 帧结构：从站地址(1) + 功能码(1) + 起始地址(2) + 寄存器数量(2) + CRC(2)
                frame.AddRange(dataBuffer);
            }
            // 计算CRC
            ushort crc = CalculateCrc(frame.ToArray(), frame.Count);
            frame.Add((byte)(crc & 0xFF));
            frame.Add((byte)(crc >> 8));
            return frame.ToArray();
        }

        /// <summary>
        /// 构建读取请求报文
        /// </summary>
        /// <param name="functionCode"></param>
        /// <param name="startAddress"></param>
        /// <param name="quantity"></param>
        /// <param name="slaveId"></param>
        /// <returns></returns>
        /// <exception cref="ArgumentException"></exception>
        public byte[] BuildReadRequest(FunctionCode functionCode, ushort startAddress, ushort count, byte slaveId)
        {
            // 帧结构：从站地址(1) + 功能码(1) + 起始地址(2) + 寄存器数量(2) + CRC(2)
            List<byte> frame = new List<byte>();
            frame.Add(slaveId);
            frame.Add((byte)functionCode);
            frame.Add(BitConverter.GetBytes(startAddress)[1]);
            frame.Add(BitConverter.GetBytes(startAddress)[0]);
            frame.Add(BitConverter.GetBytes(count)[1]);
            frame.Add(BitConverter.GetBytes(count)[0]);
            // 计算CRC
            ushort crc = CalculateCrc(frame.ToArray(), frame.Count);
            frame.Add((byte)(crc & 0xFF));
            frame.Add((byte)(crc >> 8));
            return frame.ToArray();
        }

        private ushort CalculateCrc(byte[] data, int length)
        {
            ushort crc = 0xFFFF;

            for (int i = 0; i < length; i++)
            {
                crc ^= (ushort)(data[i] & 0xFF);

                for (int j = 0; j < 8; j++)
                {
                    if ((crc & 0x0001) != 0)
                    {
                        crc >>= 1;
                        crc ^= 0xA001;
                    }
                    else
                    {
                        crc >>= 1;
                    }
                }
            }
            return crc;
        }

        /// <summary>
        /// 解析读取返回的数据
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="dataBuffer"></param>
        /// <param name="functionCode"></param>
        /// <param name="count"></param>
        /// <param name="slaveAddress"></param>
        /// <returns></returns>
        public async Task<ModbusResponse<T>> ReadResponse<T>(byte[]? dataBuffer, FunctionCode functionCode, ushort count, byte slaveAddress)
        {
            ModbusResponse<T> modbusResponse = new ModbusResponse<T>(functionCode: functionCode);
            try
            {
                if (dataBuffer == null)
                {
                    modbusResponse.State = false;
                    modbusResponse.Message = $"未响应";
                    return modbusResponse;
                }

                // 检查从站地址
                if (dataBuffer[0] != slaveAddress)
                {
                    modbusResponse.State = false;
                    modbusResponse.Message = $"{dataBuffer[0]}，从站地址不匹配";
                    return modbusResponse;
                }
                // 检查功能码
                if (dataBuffer[1] != (byte)functionCode)
                {
                    modbusResponse.State = false;
                    if ((dataBuffer[1] & 0x80) == 0x80)
                    {
                        modbusResponse.Message = $"设备返回错误: 错误码 0x{dataBuffer[2]:X2}";
                        return modbusResponse;
                    }
                    modbusResponse.Message = "功能码不匹配";
                    return modbusResponse;
                }

                // 检查CRC
                int dataLength = 3 + dataBuffer[2]; // 地址(1) + 功能码(1) + 数据长度(1) + 数据(n)
                ushort receivedCrc = (ushort)((dataBuffer[dataLength + 1] << 8) | dataBuffer[dataLength]);
                ushort calculatedCrc = CalculateCrc(dataBuffer, dataLength);

                if (receivedCrc != calculatedCrc)
                {
                    modbusResponse.State = false;
                    modbusResponse.Message = $"{receivedCrc}，CRC不匹配";
                    return modbusResponse;
                }
                // 提取并转换数据
                byte[] data = dataBuffer.ToList().GetRange(3, count * 2).ToArray();
                modbusResponse.Buffer = data.ByteFormat(_endianFormat);
                if (typeof(T) != typeof(string))
                {
                    modbusResponse.ValueArray = ByteHelper.GetResult<T>(data, _endianFormat);
                    int typeLength = Marshal.SizeOf(typeof(T));
                    if (typeLength == (count * 2))
                    {
                        modbusResponse.Value = modbusResponse.ValueArray[0];
                    }
                }
                else
                {
                    if (_stringFormat == StringFormat.ASCII)
                    {
                        modbusResponse.ValueArray = new T[] { (T)(object)Encoding.ASCII.GetString(modbusResponse.Buffer, 0, modbusResponse.Buffer.Length) };
                    }
                    else
                    {
                        modbusResponse.ValueArray = new T[] { (T)(object)Encoding.UTF8.GetString(modbusResponse.Buffer, 0, modbusResponse.Buffer.Length) };
                    }
                    modbusResponse.Value = modbusResponse.ValueArray[0];
                }
            }
            catch (Exception ex)
            {
                modbusResponse.State = false;
                modbusResponse.Message = ex.Message;
            }
            return modbusResponse;
        }

        /// <summary>
        /// 验证写入操作的响应
        /// </summary>
        /// <param name="response"></param>
        /// <param name="bytesRead"></param>
        /// <param name="expectedStartAddress"></param>
        /// <param name="expectedCount"></param>
        /// <returns></returns>
        public async Task<ModbusResponse> ValidateWriteResponse(byte[]? response, FunctionCode functionCode, ushort address, byte slaveAddress)
        {
            ModbusResponse modbusResponse = new ModbusResponse();
            if (response == null)
            {
                modbusResponse.State = false;
                modbusResponse.Message = $"未响应";
                return modbusResponse;
            }
            // 检查从站地址
            if (response[0] != slaveAddress)
            {
                modbusResponse.State = false;
                modbusResponse.Message = $"{response[0]}，从站地址不匹配";
                return modbusResponse;
            }

            // 检查功能码
            if (response[1] != (byte)functionCode)
            {
                modbusResponse.State = false;
                if ((response[1] & 0x80) == 0x80)
                {
                    modbusResponse.Message = $"设备返回错误: 错误码 0x{response[2]:X2}";
                    return modbusResponse;
                }
                modbusResponse.Message = $"{response[1]}，功能码不匹配";
                return modbusResponse;
            }

            // 检查起始地址是否正确
            ushort responseAddress = (ushort)((response[2] << 8) | response[3]);
            if (responseAddress != address)
            {
                modbusResponse.State = false;
                modbusResponse.Message = $"响应地址不匹配，预期0x{address:X4}，实际0x{responseAddress:X4}";
                return modbusResponse;
            }
            // 检查CRC
            ushort receivedCrc = (ushort)((response[7] << 8) | response[6]);
            ushort calculatedCrc = CalculateCrc(response, 6);

            if (receivedCrc != calculatedCrc)
            {
                modbusResponse.State = false;
                modbusResponse.Message = "CRC校验码不匹配";
            }
            return modbusResponse;
        }

        /// <summary>
        /// 发送数据
        /// </summary>
        /// <param name="serialPort"></param>
        /// <param name="buffer"></param>
        public void SendData(SerialPort serialPort, byte[] buffer)
        {
            serialPort.DiscardInBuffer();
            serialPort.DiscardOutBuffer();
            serialPort.Write(buffer, 0, buffer.Length);
        }

        /// <summary>
        /// 接收数据
        /// </summary>
        /// <param name="serialPort"></param>
        /// <param name="outTime"></param>
        /// <returns></returns>
        public async Task<byte[]?> Receive(SerialPort serialPort, int responseLength, int outTime = 3000)
        {
            int time = 0;
            while (serialPort.BytesToRead < 8)
            {
                await Task.Delay(5);
                time += 10;
                //超时
                if (time >= 3 * 1000)
                {
                    return null;
                }
            }
            // 读取响应
            byte[] response = new byte[responseLength];
            int bytesRead = serialPort.Read(response, 0, responseLength);
            return response;
        }
    }
}
